import { ComponentFixture, TestBed, fakeAsync, tick, waitForAsync } from '@angular/core/testing';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component } from '@angular/core';
import { InputTagItem } from './interface';
import { InputTagModule } from './input-tag.module';
import { FormsModule } from '@angular/forms';
import { LvSizeLD } from '../core/types';
import { TypeUtils } from '../core/api';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { By } from '@angular/platform-browser';

@Component({
  template: `
    <lv-input-tag
      [lvAutoFocus]="lvAutoFocus"
      [lvClearIcon]="lvClearIcon"
      [lvDisabled]="lvDisabled"
      [lvPlaceholder]="lvPlaceholder"
      [lvSize]="lvSize"
      [lvSaveOnBlur]="lvSaveOnBlur"
      [lvSeparator]="lvSeparator"
      [lvOnRemove]="lvOnRemove"
      [lvOnPressEnter]="lvOnPressEnter"
      [(ngModel)]="value"
      (ngModelChange)="valueChange($event)"
      (lvOnChange)="onChange($event)"
    ></lv-input-tag>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
class TestInputTagComponent {
  lvAutoFocus: boolean;
  lvClearIcon: string | boolean;
  lvDisabled: boolean;
  lvPlaceholder: string;
  lvSize: LvSizeLD;
  lvSaveOnBlur: boolean;
  lvSeparator: string[];
  lvOnRemove: (value: (string | InputTagItem)[]) => boolean | void;
  lvOnPressEnter: (e: KeyboardEvent) => void;

  value: (InputTagItem | string)[];
  valueChangeValue: (InputTagItem | string)[];
  data: { value: string[]; tagValue: (string | InputTagItem)[]; e: Event };

  valueChange(value: (InputTagItem | string)[]): void {
    this.valueChangeValue = value;
  }

  onChange(data: { value: string[]; tagValue: (string | InputTagItem)[]; e: Event }): void {
    this.data = data;
  }
}

describe('InputTagComponent', () => {
  beforeEach(waitForAsync(() => {
    TestBed.configureTestingModule({
      declarations: [TestInputTagComponent],
      imports: [InputTagModule, FormsModule, NoopAnimationsModule],
    }).compileComponents();
  }));

  let component: TestInputTagComponent;
  let fixture: ComponentFixture<TestInputTagComponent>;
  let cdr: ChangeDetectorRef;

  const renderComponent = (param?) => {
    for (const [key, value] of Object.entries(param)) {
      if (!TypeUtils.isUndefined(value)) {
        component[key] = value;
      }
    }
    fixture.detectChanges();
  };

  describe('render', () => {
    beforeEach(() => {
      fixture = TestBed.createComponent(TestInputTagComponent);
      component = fixture.componentInstance;
    });

    it('default', () => {
      renderComponent({});
      expect(fixture).toMatchSnapshot();
    });

    it('size', () => {
      renderComponent({ lvSize: 'large' });
      expect(fixture).toMatchSnapshot();
    });

    it('disabled', () => {
      renderComponent({ lvDisabled: true });
      expect(fixture).toMatchSnapshot();
    });

    it('placeHolder', () => {
      renderComponent({ lvPlaceholder: 'Basic Use' });
      expect(fixture).toMatchSnapshot();
    });
  });

  describe('function', () => {
    beforeEach(() => {
      fixture = TestBed.createComponent(TestInputTagComponent);
      component = fixture.componentInstance;
      cdr = fixture.debugElement.injector.get(ChangeDetectorRef);
    });

    it('autoFocus', fakeAsync(() => {
      renderComponent({ lvAutoFocus: true });
      tick(250);
      cdr.detectChanges();
      tick(250);
      cdr.detectChanges();
      expect(document.activeElement === document.querySelector('input')).toBeTruthy();
    }));

    it('clear', fakeAsync(() => {
      renderComponent({ lvClearIcon: true, value: ['tag1', 'tag2', 'tag3'] });
      fixture.debugElement.query(By.css('.lv-input-tag')).triggerEventHandler('mouseenter');
      tick(200);
      cdr.detectChanges();
      const clearIcon = fixture.debugElement.query(By.css('.lv-input-tag-clear i'));
      expect(clearIcon).toBeTruthy();
      clearIcon.triggerEventHandler('click');
      cdr.detectChanges();
      expect(component.data.value.length).toBe(0);
    }));

    it('saveOnBlur', fakeAsync(() => {
      renderComponent({ lvSaveOnBlur: true, lvAutoFocus: true });
      tick(250);
      cdr.detectChanges();
      tick(250);
      cdr.detectChanges();
      const input = fixture.debugElement.query(By.css('input'));
      input.nativeElement.value = '123';
      input.nativeElement.dispatchEvent(new Event('input'));
      input.triggerEventHandler('blur');
      cdr.detectChanges();
      expect(component.data.value[0]).toBe('123');
    }));

    it('separator', fakeAsync(() => {
      renderComponent({ lvSeparator: [',', '，', ';'], lvAutoFocus: true, lvSaveOnBlur: true });
      tick(250);
      cdr.detectChanges();
      tick(250);
      cdr.detectChanges();
      const input = fixture.debugElement.query(By.css('input'));
      input.nativeElement.value = '1,2，3;4';
      input.nativeElement.dispatchEvent(new Event('input'));
      input.triggerEventHandler('blur');
      cdr.detectChanges();
      expect(component.data.value.join(';')).toBe('1;2;3;4');
    }));
  });
});
